/**
 * @fileoverview
 * This script defines utilities for interacting with data from flybase.
 * @author <a href="http://purl.org/net/aliman">Alistair Miles</a>
 * @version $Revision:538 $ on $Date: 2008-08-27 09:08:41 +0100 (Wed, 27 Aug 2008) $ by $Author: aliman $
 * @requires flyui.util
 * @requires YAHOO.util.Connect
 * TODO license terms
 */


// create a namespace if not already defined
flyui.namespace("flyui.flybase");


/**
 * Create a service object.
 * @class
 * A wrapper for a flybase SPARQL endpoint.
 * @extends flyui.sparql.Service
 * @constructor 
 * @param {String} endpointURL the location of the flybase SPARQL endpoint
 */
flyui.flybase.Service = function( endpointURL ) {
	
	/**
	 * @private
	 */
	this._endpoint = endpointURL;
	
};


// extend
flyui.flybase.Service.prototype = new flyui.sparql.Service();


/**
 * Find genes by any name, for example "FBgn123456", "CG1234", "aly".
 * @param {String} anyName the gene name to search by
 * @param {Function} success the success case callback function, which must accept an array of Gene objects
 * @param {Function} failure the failure case callback function, which must accept a YUI HTTP response object
 */
flyui.flybase.Service.prototype.findGenesByAnyName = function( anyName, success, failure ) {
	var _context = "flyui.flybase.Service.prototype.findGenesByAnyName";
	try {
        flyui.info("request: "+anyName, _context);
        var chain = flyui.chain(flyui.flybase.Service.responseToGenes, success);
        var query = flyui.flybase.Service._buildQueryForFindGenesByAnyName(anyName);
        this.query(query, chain, failure);    
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    
};


/**
 * Find genes by any name, for example "FBgn123456", "CG1234", "aly", using a case-insensitive comparison.
 * @param {String} anyName the gene name to search by
 * @param {Function} success the success case callback function, which must accept an array of Gene objects
 * @param {Function} failure the failure case callback function, which must accept a YUI HTTP response object
 */
flyui.flybase.Service.prototype.findGenesByAnyNameCaseInsensitive = function( anyName, success, failure ) {
    var _context = "flyui.flybase.Service.prototype.findGenesByAnyNameCaseInsensitive";
    try {
        flyui.info("request: "+anyName, _context);
        var successChain = flyui.chain(flyui.flybase.Service.responseToGenes, success);
        var query = flyui.flybase.Service._buildQueryForFindGenesByAnyNameCaseInsensitive(anyName);
        this.query(query, successChain, failure);
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    
}


/**
 * @param {Object} response
 * @type Array<flyui.flybase.Gene>
 * @private
 * @static
 */
flyui.flybase.Service.responseToGenes = function( response ) {
    var _context = "flyui.flybase.Service.responseToGenes";
    try {
        flyui.debug("response status: "+response.status, _context);
        
        flyui.debug("try parsing response text as json", _context);
        flyui.debug("parsing response: "+response.responseText, _context);
        var resultSet = YAHOO.lang.JSON.parse(response.responseText);
        
        flyui.debug("convert result set to an array of genes", _context);
        var genes = flyui.flybase.Gene.newInstancesFromSPARQLResults(resultSet);
        
        return genes;
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    
};


/**
 * Build a SPARQL query to retrieve genes by any name.
 * @param {String} anyName the gene name to search by
 * @return a SPARQL query
 * @type String
 * @private
 * @static
 */
flyui.flybase.Service._buildQueryForFindGenesByAnyName = function( anyName ) {
    var _context = "flyui.flybase.Service._buildQueryForFindGenesByAnyName";
    try {
    //  var prefixes =  "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
    //                  "PREFIX flybase: <http://rodos.zoo.ox.ac.uk/2008/flyweb/ontologies/flybase/> ";
    //
    //  var body =      "SELECT * WHERE { " +
    //                      "?gene flybase:anyname '"+anyName+"'^^xsd:string; " + 
    //                          "flybase:flybaseID ?flybaseID. " + 
    //                      "OPTIONAL {?gene flybase:symbol ?symbol} " + 
    //                      "OPTIONAL {?gene flybase:annotationSymbol ?annotationSymbol} " + 
    //                      "OPTIONAL {?gene flybase:fullname ?fullName} " + 
    //                      "OPTIONAL {?gene flybase:synonym ?synonym} " + 
    //                  "}";
    
        var prefixes =  "PREFIX chado: <http://purl.org/net/chado/schema/> " +
                        "PREFIX fbutil: <http://purl.org/net/openflydata/flybase-utils/> " +
                        "PREFIX syntype: <http://purl.org/net/flybase/synonym-types/> " +
                        "PREFIX xs: <http://www.w3.org/2001/XMLSchema#> ";
                        
        var body =      "SELECT * WHERE { " +
                        "?gene fbutil:anyName \""+anyName+"\"^^xs:string ; " + 
                        "    a chado:Feature ; " +
                        "    chado:name ?symbol ; " +
                        "    chado:uniquename ?flybaseID . " +
                        "OPTIONAL { ?gene chado:dbxref [ chado:accession ?annotationSymbol ] . } " +
                        "OPTIONAL { ?gene chado:synonym [ chado:name ?synonym ] . } " +
                        "OPTIONAL { ?gene chado:synonym [ a syntype:FullName ; chado:name ?fullName ] . } " +
                        "}"; 
    
        var query = prefixes + body;
    
        return query;
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    

};


/**
 * Build a SPARQL query to retrieve genes by any name.
 * @param {String} anyName the gene name to search by
 * @return a SPARQL query
 * @type String
 * @private
 * @static
 */
flyui.flybase.Service._buildQueryForFindGenesByAnyNameCaseInsensitive = function( anyName ) {
    var _context = "flyui.flybase.Service._buildQueryForFindGenesByAnyNameCaseInsensitive";
    try {
    //  var prefixes =  "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
    //                  "PREFIX flybase: <http://rodos.zoo.ox.ac.uk/2008/flyweb/ontologies/flybase/> ";
    //
    //  var body =      "SELECT * WHERE { " +
    //                      "?gene flybase:anyNameLowerCase '"+anyName.toLowerCase()+"'^^xsd:string; " + 
    //                          "flybase:flybaseID ?flybaseID. " + 
    //                      "OPTIONAL {?gene flybase:symbol ?symbol} " + 
    //                      "OPTIONAL {?gene flybase:annotationSymbol ?annotationSymbol} " + 
    //                      "OPTIONAL {?gene flybase:fullname ?fullName} " + 
    //                      "OPTIONAL {?gene flybase:synonym ?synonym} " + 
    //                  "}";
    
        var prefixes =  "PREFIX chado: <http://purl.org/net/chado/schema/> " +
                        "PREFIX fbutil: <http://purl.org/net/openflydata/flybase-utils/> " +
                        "PREFIX syntype: <http://purl.org/net/flybase/synonym-types/> " +
                        "PREFIX xs: <http://www.w3.org/2001/XMLSchema#> ";
                        
        var body =      "SELECT * WHERE { " +
                        "?gene fbutil:anyNameLowerCase \""+anyName.toLowerCase()+"\"^^xs:string ; " + 
                        "    a chado:Feature ; " +
                        "    chado:name ?symbol ; " +
                        "    chado:uniquename ?flybaseID . " +
                        "OPTIONAL { ?gene chado:dbxref [ chado:accession ?annotationSymbol ] . } " +
                        "OPTIONAL { ?gene chado:synonym [ chado:name ?synonym ] . } " +
                        "OPTIONAL { ?gene chado:synonym [ a syntype:FullName ; chado:name ?fullName ] . } " +
                        "}"; 
    
        var query = prefixes + body;
    
        return query;    
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    
};


/**
 * Construct a new gene object.
 * @class
 * A Gene object.
 * @constructor
 */
flyui.flybase.Gene = function() {
	
	var that = this;

	/**
	 * @type String
	 */
	this.flybaseID = null;
	
	/**
	 * @type Array
	 */
	this.symbols = new Array();

	/**
	 * @type Array
	 */
	this.annotationSymbols = new Array();

	/**
	 * @type Array
	 */
	this.fullNames = new Array();

	/**
	 * @type Array
	 */
	this.synonyms = new Array();

	/** 
	 * Add a symbol for this gene.
	 * @param {String} symbol e.g. "TODO"
	 */
	this.addSymbol = function( symbol ) {
		flyui.util.appendIfNotMember(that.symbols, symbol);
	};

	/** 
	 * Add an annotation symbol for this gene.
	 * @param {String} annotationSymbol e.g. "TODO"
	 */
	this.addAnnotationSymbol = function( annotationSymbol ) {
		flyui.util.appendIfNotMember(that.annotationSymbols, annotationSymbol);
	};


	/** 
	 * Add a full name for this gene.
	 * @param {String} fullName e.g. "TODO"
	 */
	this.addFullName = function( fullName ) {
		flyui.util.appendIfNotMember(that.fullNames, fullName);
	};


	/** 
	 * Add a synonym for this gene.
	 * @param {String} synonym e.g. "TODO"
	 */
	this.addSynonym = function( synonym ) {
		flyui.util.appendIfNotMember(that.synonyms, synonym);
	};

};


/**
 * Create an array of gene objects from a SPARQL result set obejct.
 * @param {Object} resultSet a SPARQL result set
 * @return an array of Gene objects
 * @type Array
 */
flyui.flybase.Gene.newInstancesFromSPARQLResults = function( resultSet ) {
    var _context = "flyui.flybase.Gene.newInstancesFromSPARQLResults";
    try {
        var pool = new flyui.flybase.GenePool();
    
        var bindings = resultSet.results.bindings;
        for (var i in bindings) {
            var flybaseID = bindings[i].flybaseID.value;
            var gene = pool.get(flybaseID);
            if (bindings[i].symbol) {
                gene.addSymbol(bindings[i].symbol.value);
            }
            if (bindings[i].annotationSymbol) {
                gene.addAnnotationSymbol(bindings[i].annotationSymbol.value);
            }
            if (bindings[i].fullName) {
                gene.addFullName(bindings[i].fullName.value);
            }
            if (bindings[i].synonym) {
                gene.addSynonym(bindings[i].synonym.value);
            }
        }
    
        return pool.toArray();
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    
};


/**
 * Create a pool of gene objects.
 * @class
 * A pool of gene objects.
 * @constructor
 */
flyui.flybase.GenePool = function() {
	
	/**
	 * @private
	 */
	this._pool = new Object();

};


/**
 * Get a gene from the pool, or create a new one if it doesn't exist.
 * @param {String} flybaseID 
 * @return a flyui.flybase.Gene object
 */
flyui.flybase.GenePool.prototype.get = function( flybaseID ) {
    var _context = "flyui.flybase.GenePool.prototype.get";
    try {
        var gene = this._pool[flybaseID];
        
        if ( typeof gene == "undefined" || !gene ) {
            gene = new flyui.flybase.Gene();
            gene.flybaseID = flybaseID;
            this._pool[flybaseID] = gene;   
        }
        
        return gene;
    
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    	
};


flyui.flybase.GenePool.prototype.toArray = function() {
    var _context = "flyui.flybase.GenePool.prototype.toArray";
    try {
        var array = new Array();
        for (var key in this._pool) {
            array[array.length] = this._pool[key];
        }
        return array;
    } catch (error) {
        flyui.debug("wrap and rethrow error to get a stack trace", _context);
        throw new flyui.UnexpectedException(_context, error);
    }    
}

